//+--------------------------------------------------------------------
//| Copyright(c) 2012 - an.vuongngocduy					
//| This file is a part of Compact Game Engine (CGE)	
//+--------------------------------------------------------------------
#include "cgeImage.h"

namespace cge
{
	namespace gfx
	{
		/*----------------------------------------------------------------------------------------*/
		Image::Image()
			: mData(null), mDimension(0,0), mFormat(PixelFormat::Unknown)
		{}

		/*----------------------------------------------------------------------------------------*/
		Image::Image(const Image& img)
		{
			*this = img;
		}

		/*----------------------------------------------------------------------------------------*/
		Image::Image(EPixelFormat format, const Size2u& dimension)
			: mData(null)
		{
			mDimension = dimension;
			mFormat = format;
			mData = mAlloc.Allocate(BitsPerPixel() / 8, mDimension.Width * mDimension.Height);
			if(mData == null) //out of memory
			{
				mDimension.Set(0,0);
				mFormat = PixelFormat::Unknown;
			}
		}

		/*----------------------------------------------------------------------------------------*/
		Image::Image(const u8* data, EPixelFormat format,  const Size2u& dimension)
			: mData(null)
		{
			if(data == null) return;		
			mDimension = dimension;
			mFormat = format;
			u32 bytesPerPixel = BitsPerPixel() / 8;
			mData = mAlloc.Allocate(bytesPerPixel, mDimension.Width * mDimension.Height);
			if(mData == null) //out of memory
			{
				mDimension.Set(0,0);
				mFormat = PixelFormat::Unknown;
				return;
			}
			memcpy((void*)mData, (void*) data, bytesPerPixel * mDimension.Width * mDimension.Height);
		}

		/*----------------------------------------------------------------------------------------*/
		Image::Image(u8* data, EPixelFormat format,  const Size2u& dimension, b8 useExternalMem)
			: mData(null)
		{
			if(data == null) return;		
			mDimension = dimension;
			mFormat = format;
			if(useExternalMem)
			{
				u32 bytesPerPixel = BitsPerPixel() / 8;
				mData = mAlloc.Allocate(bytesPerPixel, mDimension.Width * mDimension.Height);
				if(mData == null) //out of memory
				{
					mDimension.Set(0,0);
					mFormat = PixelFormat::Unknown;
					return;
				}
				memcpy((void*)mData, (void*) data, bytesPerPixel * mDimension.Width * mDimension.Height);
			}
			else
			{
				mData = data;
			}
		}

		/*----------------------------------------------------------------------------------------*/
		Image::~Image()
		{
			Dispose();
		}

		/*----------------------------------------------------------------------------------------*/
		EPixelFormat Image::PixelFormat() const
		{	
			return mFormat;
		}

		/*----------------------------------------------------------------------------------------*/
		u32 Image::Size() const
		{
			u32 bytesPerPixel = BitsPerPixel() / 8;
			u32 size = mDimension.Width * mDimension.Height * bytesPerPixel;
			return size;
		}

		/*----------------------------------------------------------------------------------------*/
		u32 Image::Width() const
		{
			return mDimension.Width;
		}

		/*----------------------------------------------------------------------------------------*/
		u32 Image::Height() const
		{
			return mDimension.Height;
		}

		/*----------------------------------------------------------------------------------------*/
		u32 Image::BitsPerPixel() const
		{
			switch(mFormat)
			{
			case PixelFormat::Bpp16_GreyScale:
			case PixelFormat::Bpp16_R5G6B5:
			case PixelFormat::Bpp16_R5G5B5A1:
			case PixelFormat::Bpp16_R4G4B4A4:	
				return 16;
				
			case PixelFormat::Bpp24_R8G8B8:
				return 24;

			case PixelFormat::Bpp32_R8G8B8A8:
				return 32;
			}
		}

		/*----------------------------------------------------------------------------------------*/
		const u8* Image::Data() const
		{
			return mData;
		}

		u8* Image::Data()
		{
			return mData;
		}

		/*----------------------------------------------------------------------------------------*/
		Color Image::GetPixel(u32 x, u32 y) const
		{
			Color clr;
			if(x < mDimension.Width && y < mDimension.Height)
			{
				u32 bytesPerPixel = BitsPerPixel() / 8;
				const u8* px = mData + (bytesPerPixel * (mDimension.Width * y + x));
				if(mFormat == PixelFormat::Bpp32_R8G8B8A8)
					clr.Set(px[0], px[1], px[2], px[3]);
				else if(mFormat == PixelFormat::Bpp24_R8G8B8)
					clr.Set(px[0], px[1], px[2], 0xFF);
			}
			return clr;
		}

		/*----------------------------------------------------------------------------------------*/
		Image& Image::SetPixel(const Color& col, u32 x, u32 y)
		{
			return *this;
		}

		/*----------------------------------------------------------------------------------------*/
		Image& Image::FlipVertical()
		{
			u32 bytesPerPixel = BitsPerPixel() / 8;
			u32 bytesPerRow = mDimension.Width * bytesPerPixel;
			u8* tmp = mAlloc.Allocate(bytesPerRow);
			for(u32 i = 0, j = mDimension.Height - 1; i < j ; i++ , j--)
			{
				memcpy(tmp, mData + (i * bytesPerRow), bytesPerRow);
				memcpy(mData + (i * bytesPerRow), mData + (j * bytesPerRow), bytesPerRow);
				memcpy(mData + (j * bytesPerRow), tmp, bytesPerRow);
			}
			mAlloc.Deallocate(tmp);
			return *this;
		}

		/*----------------------------------------------------------------------------------------*/
		Image& Image::FlipHorizontal()
		{
			u32 bytesPerPixel = BitsPerPixel() / 8;
			u32 bytesPerRow = mDimension.Width * bytesPerPixel;
			u8* tmp = mAlloc.Allocate(bytesPerRow);
			for(u32 i = 0; i < mDimension.Height; i++)
			{
				u8* nextRowData = mData + (i * bytesPerRow);
				for(u32 j  = 0, k = mDimension.Width - 1; j < k; j++ , k--)
					memcpy(tmp + (j * bytesPerPixel), nextRowData + (k * bytesPerPixel), bytesPerPixel);
				memcpy(nextRowData, tmp, bytesPerRow); 
			}
			mAlloc.Deallocate(tmp);
			return *this;
		}

		/*----------------------------------------------------------------------------------------*/
		void Image::Dispose()
		{
			if(mData)
				mAlloc.Deallocate(mData);
			mData = null;
		}

		/*----------------------------------------------------------------------------------------*/
		Image& Image::operator = (const Image& img)
		{
			if(mData == img.mData)
				return *this;
			
			u32 size = img.Size();

			if(size != Size())
			{
				Dispose();
				mData = mAlloc.Allocate(size);
			}

			mFormat = img.mFormat;
			mDimension = img.mDimension;
			memcpy(mData, img.mData, size);
		}
	}
}